(function ( exports ) {

    //region Retrieve

    /**
     * Retrieve response
     * @prop {COSA.BusinessEntity} entity Entity
     * @memberOf COSA.Response
     * @constructor Retrieve
     */
    function Retrieve () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.Retrieve);

        this.entity = null;
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, Retrieve);

    Retrieve.prototype.parseResponse = function ( responseBody ) {
        this.entity = (new COSA.BusinessEntity())
            .deserialize((responseBody.keyValueCollection()).Entity);
    };

    //endregion Retrieve

    //region RetrieveMultiple


    /**
     * RetrieveMultiple response
     * @prop {COSA.EntityCollection} entities Entities
     * @memberOf COSA.Response
     * @constructor RetrieveMultiple
     */
    function RetrieveMultiple ( ) {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.RetrieveMultiple);

        this.entities = null;
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, RetrieveMultiple);

    RetrieveMultiple.prototype.parseResponse = function ( responseBody ) {
        this.entities = (new COSA.EntityCollection())
            .deserialize((responseBody.keyValueCollection()).EntityCollection);
    };

    //endregion RetrieveMultiple

    //region Create

    /**
     * Create response
     * @prop {string} id Created entity unique identifier (GUID)
     * @memberOf COSA.Response
     * @constructor Create
     */
    function Create ( ) {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.Create);
        this.id = null;
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, Create);

    Create.prototype.parseResponse = function ( responseBody ) {
        this.id = responseBody.node('KeyValuePairOfstringanyType.value').text();
    };

    //endregion Create

    //region Update

    /**
     * Update response
     * @memberOf COSA.Response
     * @constructor Update
     */
    function Update () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.Update);
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, Update);

    //endregion Update

    //region ExecuteMultiple

    /**
     * ExecuteMultiple response
     * @param initialRequest Initial request
     * @prop initialRequests Requests
     * @prop {COSA.ResponseCollection} responses Responses
     * @memberOf COSA.Response
     * @constructor ExecuteMultiple
     */
    function ExecuteMultiple ( initialRequest ) {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.ExecuteMultiple);
        this.initialRequests = initialRequest;
        this.responses = [];
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, ExecuteMultiple);

    ExecuteMultiple.prototype.parseResponse = function ( responseBody ) {
        var keys = responseBody.keyValueCollection(), responseItems, _this = this;
        this.isFaulted = keys.IsFaulted.text() == 'true';

        responseItems = keys.Responses.nodes('ExecuteMultipleResponseItem');

        this.responses = responseItems.map(function ( item, index ) {
            return _this.initialRequests[index].deserializeMultiple(item);
        });
    };

    //endregion ExecuteMultiple

    //region Delete

    /**
     * Delete response
     * @memberOf COSA.Response
     * @constructor Delete
     */
    function Delete () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.Delete);
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, Delete);

    //endregion Delete

    //region Associate

    /**
     * Associate response
     * @memberOf COSA.Response
     * @constructor Associate
     */
    function Associate () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.Associate);
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, Associate);

    //endregion Associate

    //region Disassociate

    /**
     * Disassociate response
     * @memberOf COSA.Response
     * @constructor Disassociate
     */
    function Disassociate () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.Disassociate);
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, Disassociate);

    //endregion Disassociate

    //region RetrieveAttribute

    /**
     * RetrieveAttribute response
     * @prop {COSA.AttributeMetadata} attributeMetadata Attribute metadata
     * @memberOf COSA.Response
     * @constructor RetrieveAttribute
     */
    function RetrieveAttribute ( ) {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.RetrieveAttribute);

        this.attributeMetadata = null;
    }

    COSA.Util.derivePrototype(COSA.Response.ResponseBase, RetrieveAttribute);

    RetrieveAttribute.prototype.parseResponse = function ( responseBody ) {
        this.attributeMetadata = (new COSA.AttributeMetadata())
            .deserialize((responseBody.keyValueCollection()).AttributeMetadata);
    };

    //endregion RetrieveAttribute

    //region RetrieveEntity

    /**
     * RetrieveEntity response
     * @prop {COSA.EntityMetadata} entityMetadata Entity Metadata
     * @memberOf COSA.Response
     * @constructor RetrieveEntity
     */
    function RetrieveEntity () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.RetrieveEntity);
        this.entityMetadata = null;
    }

    COSA.Util.deriveOverride(COSA.Response.ResponseBase, RetrieveEntity, {
        'parseResponse': function ( responseBody ) {
            this.entityMetadata = (new COSA.EntityMetadata())
                .deserialize((responseBody.keyValueCollection()).EntityMetadata);
        }
    });

    //endregion RetrieveEntity

    //region RetrieveAllEntities

    /**
     * RetrieveAllEntities response
     * @prop {COSA.EntityMetadata} entityMetadata Entity Metadata
     * @prop {string} timestamp Timestamp
     * @memberOf COSA.Response
     * @constructor RetrieveAllEntities
     */
    function RetrieveAllEntities () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.RetrieveAllEntities);
        this.entityMetadata = null;
        this.timestamp = '';
    }

    COSA.Util.deriveOverride(COSA.Response.ResponseBase, RetrieveAllEntities, {
        parseResponse: function ( responseBody ) {
            var kv = responseBody.keyValueCollection();
            this.entityMetadata = (new COSA.EntityMetadataCollection()).deserialize(kv.EntityMetadata);
            this.timestamp = kv.Timestamp.text();
        }
    });

    //endregion RetrieveAllEntities

    //region RetrieveMetadataChanges

    /**
     * RetrieveMetadataChanges response
     * @prop {COSA.EntityMetadata} entityMetadata Entity Metadata
     * @prop deletedMetadata Deleted metadata
     * @prop {string} serverVersionStamp Server version stamp
     * @memberOf COSA.Response
     * @constructor RetrieveMetadataChanges
     */
    function RetrieveMetadataChanges () {
        COSA.Response.ResponseBase.call(this, COSA.Const.Message.RetrieveMetadataChanges);
        this.entityMetadata = null;
        this.deletedMetadata = null;
        this.serverVersionStamp = '';
    }

    COSA.Util.deriveOverride(COSA.Response.ResponseBase, RetrieveMetadataChanges, {
        parseResponse: function ( responseBody ) {
            var propertyNodes = responseBody.keyValueCollection();

            this.entityMetadata = (new COSA.EntityMetadataCollection()).deserialize(propertyNodes.EntityMetadata);
            this.serverVersionStamp = propertyNodes.ServerVersionStamp.text();
        }
    });

    //endregion RetrieveMetadataChanges

    exports.Create = Create;
    exports.Update = Update;
    exports.Delete = Delete;
    exports.Associate = Associate;
    exports.Disassociate = Disassociate;
    exports.ExecuteMultiple = ExecuteMultiple;
    exports.Retrieve = Retrieve;
    exports.RetrieveMultiple = RetrieveMultiple;
    exports.RetrieveAttribute = RetrieveAttribute;
    exports.RetrieveEntity = RetrieveEntity;
    exports.RetrieveAllEntities = RetrieveAllEntities;
    exports.RetrieveMetadataChanges = RetrieveMetadataChanges;

})(COSA.Response);
